// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
 *  Copyright 2010-2011 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <fstream>
#include <string>
#include "ImageReaderSource.h"
#include <zxing/common/Counted.h>
#include <zxing/Binarizer.h>
#include <zxing/MultiFormatReader.h>
#include <zxing/Result.h>
#include <zxing/ReaderException.h>
#include <zxing/common/GlobalHistogramBinarizer.h>
#include <zxing/common/HybridBinarizer.h>
#include <exception>
#include <zxing/Exception.h>
#include <zxing/common/IllegalArgumentException.h>
#include <zxing/BinaryBitmap.h>
#include <zxing/DecodeHints.h>

#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/multi/qrcode/QRCodeMultiReader.h>
#include <zxing/multi/ByQuadrantReader.h>
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/multi/GenericMultipleBarcodeReader.h>

using namespace std;
using namespace zxing;
using namespace zxing::multi;
using namespace zxing::qrcode;


#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <cctype>
#include <opencv/cv.h>
#include <opencv/highgui.h>


#include <opencv2/core/core.hpp>

using namespace cv;
namespace {

bool more = false;
bool test_mode = false;
bool try_harder = false;
bool search_multi = false;
bool use_hybrid = false;
bool use_global = false;
bool verbose = false;

}

vector<Ref<Result> > decode(Ref<BinaryBitmap> image, DecodeHints hints) {
  Ref<Reader> reader(new MultiFormatReader);
  return vector<Ref<Result> >(1, reader->decode(image, hints));
}

vector<Ref<Result> > decode_multi(Ref<BinaryBitmap> image, DecodeHints hints) {
  MultiFormatReader delegate;
  GenericMultipleBarcodeReader reader(delegate);
  return reader.decodeMultiple(image, hints);
}

int read_image(Ref<LuminanceSource> source, bool hybrid, string expected) {
  vector<Ref<Result> > results;
  string cell_result;
  int res = -1;

  try {
    Ref<Binarizer> binarizer;
    if (hybrid) {
      binarizer = new HybridBinarizer(source);
    } else {
      binarizer = new GlobalHistogramBinarizer(source);
    }
    DecodeHints hints(DecodeHints::DEFAULT_HINT);
    hints.setTryHarder(try_harder);
    Ref<BinaryBitmap> binary(new BinaryBitmap(binarizer));
    if (search_multi) {
      results = decode_multi(binary, hints);
    } else {
      results = decode(binary, hints);
    }
    res = 0;
  } catch (const ReaderException& e) {
    cell_result = "zxing::ReaderException: " + string(e.what());
    res = -2;
  } catch (const zxing::IllegalArgumentException& e) {
    cell_result = "zxing::IllegalArgumentException: " + string(e.what());
    res = -3;
  } catch (const zxing::Exception& e) {
    cell_result = "zxing::Exception: " + string(e.what());
    res = -4;
  } catch (const std::exception& e) {
    cell_result = "std::exception: " + string(e.what());
    res = -5;
  }

  if (test_mode && results.size() == 1) {
    std::string result = results[0]->getText()->getText();
    if (expected.empty()) {
      cout << "  Expected text or binary data for image missing." << endl
           << "  Detected: " << result << endl;
      res = -6;
    } else {
      if (expected.compare(result) != 0) {
        cout << "  Expected: " << expected << endl
             << "  Detected: " << result << endl;
        cell_result = "data did not match";
        res = -6;
      }
    }
  }

  if (res != 0 && (verbose || (use_global ^ use_hybrid))) {
    cout << (hybrid ? "Hybrid" : "Global")
         << " binarizer failed: " << cell_result << endl;
  } else if (!test_mode) {
    if (verbose) {
      cout << (hybrid ? "Hybrid" : "Global")
           << " binarizer succeeded: " << endl;
    }
    for (size_t i = 0; i < results.size(); i++) {
      if (more) {
        cout << "  Format: "
             << BarcodeFormat::barcodeFormatNames[results[i]->getBarcodeFormat()]
             << endl;
        for (int j = 0; j < results[i]->getResultPoints()->size(); j++) {
          cout << "  Point[" << j <<  "]: "
               << results[i]->getResultPoints()[j]->getX() << " "
               << results[i]->getResultPoints()[j]->getY() << endl;
        }
      }
      if (verbose) {
        cout << "    ";
      }
      cout << results[i]->getText()->getText() << endl;
    }
  }

  return res;
}

string read_expected(string imagefilename) {
  string textfilename = imagefilename;
  string::size_type dotpos = textfilename.rfind(".");

  textfilename.replace(dotpos + 1, textfilename.length() - dotpos - 1, "txt");
  ifstream textfile(textfilename.c_str(), ios::binary);
  textfilename.replace(dotpos + 1, textfilename.length() - dotpos - 1, "bin");
  ifstream binfile(textfilename.c_str(), ios::binary);
  ifstream *file = 0;
  if (textfile.is_open()) {
    file = &textfile;
  } else if (binfile.is_open()) {
    file = &binfile;
  } else {
    return std::string();
  }
  file->seekg(0, ios_base::end);
  size_t size = size_t(file->tellg());
  file->seekg(0, ios_base::beg);

  if (size == 0) {
    return std::string();
  }

  char* data = new char[size + 1];
  file->read(data, size);
  data[size] = '\0';
  string expected(data);
  delete[] data;

  return expected;
}

class OpenCVMATBitmapSource : public LuminanceSource
{
private:
	cv::Mat m_image;
public:
	OpenCVMATBitmapSource(cv::Mat image_): LuminanceSource(m_image.cols,m_image.rows)
	{
		m_image = image_.clone();
	}
	~OpenCVMATBitmapSource()
	{

	}
	int getWidth() const
	{
		return m_image.cols;
	}
	int getHeight() const
	{
		return m_image.rows;
	}
	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const
	{
		int width_ = getWidth();
		if(!row)
		{
			row = ArrayRef<char> (width_);
		}
		const unsigned char *p = m_image.ptr<uchar>(y);//m_image.data;    //取得mat的数据指针

		for(int i = 0; i < width_ * m_image.channels(); ++i, p++)
		{
			row[i] = (char)*p;
		}
		return row;
	}

	ArrayRef<char>  getMatrix()const
	{
		int width_ = getWidth();
		int	height_ = getHeight();
		int channel = m_image.channels();   //图像的通道数
		ArrayRef<char> matrix = ArrayRef<char>(width_ * height_ * channel);//创建图像数据的矩阵

		for(int y = 0; y < height_; ++y)
		{
			const unsigned char *p = m_image.ptr<uchar>(y);//m_image.data;
			for(int x = 0; x < width_ * channel; ++x,++p)
			{
				//matrix[y * width_ + x] = (char)*p;
				matrix[y * width_ * channel + x] = *p;       //存入图像数据到矩阵
			}
		}
		return matrix;
	}

};
class OpenCVBitmapSource : public LuminanceSource
{
private:
	IplImage *m_pImage;

public:
	OpenCVBitmapSource(IplImage *image): LuminanceSource(	image->width,image->height)
	{
		m_pImage = image;

	}

	~OpenCVBitmapSource()
	{
		// when supporting cropping or rotation, the call to cvReleaseImage()
		// leads to crashes. obviously m_pImage must be managed ref-counted.
		// cvReleaseImage(&m_pImage);
	}

	int getWidth() const { return m_pImage->width; }
	int getHeight() const { return m_pImage->height; }

	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const
	{
		int width_ = getWidth();
		if (!row)
			row = ArrayRef<char>(width_);
		const char *p = m_pImage->imageData + y*m_pImage->widthStep;
		for(int i = 0; i < width_; ++i,++p)
		{
			row[i] = *p;
		}
		return row;
	}

	ArrayRef<char>  getMatrix()const
	{
		int width_ = getWidth();
		int height_ =  getHeight();
		ArrayRef<char>  matrix = ArrayRef<char>(width_ * height_);

		for(int y = 0; y < height_; ++y)
		{
			const char *p = m_pImage->imageData+y*m_pImage->widthStep;
			for(int x = 0; x < width_; ++x,++p)
			{
				matrix[y*width_ +x] = *p;
			}
		}
		return matrix;
	}

	/*
	// The following methods are not supported by this demo (the DataMatrix Reader doesn't call these methods)
	bool isCropSupported() const { return false; }
	Ref<LuminanceSource> crop(int left, int top, int width, int height) {}
	bool isRotateSupported() const { return false; }
	Ref<LuminanceSource> rotateCounterClockwise() {}
	*/
};
int number = 0;
void decode_imageMat(Reader *reader, cv::Mat image)
{
	try
	{
		Ref<OpenCVMATBitmapSource> source(new OpenCVMATBitmapSource(image));


		Ref<Binarizer> binarizer (new HybridBinarizer(source));//(new GlobalHistogramBinarizer(source));


		Ref<BinaryBitmap> bitmap(new BinaryBitmap(binarizer));



		Ref<Result> result(reader->decode(bitmap, DecodeHints(DecodeHints::DEFAULT_HINT)));


		cout << result->getText()->getText() << endl;
		cout << "count :" << number++ <<endl;   //ŽòÓ¡³öœâÂëŽÎÊý
	}
	catch (zxing::Exception& e)
	{
		// cerr << "Error: " << e.what() << endl;
	}
}

void decode_image(Reader *reader, IplImage *image)
{
	try
	{

		Ref<OpenCVBitmapSource> source(new OpenCVBitmapSource(image));
		//图像二值化
		Ref<Binarizer> binarizer (new HybridBinarizer(source));//(new GlobalHistogramBinarizer(source));

		//图像位图
		Ref<BinaryBitmap> bitmap(new BinaryBitmap(binarizer));

		/*
		 * const DecodeHints DecodeHints::DEFAULT_HINT(
							  ONED_HINT |
							  QR_CODE_HINT |
							  DATA_MATRIX_HINT |
							  AZTEC_HINT |
							  PDF_417_HINT
							  );
		 *
		 */
		//解码开始
		Ref<Result> result(reader->decode(bitmap, DecodeHints(DecodeHints::DEFAULT_HINT)));
		cout << result->getText()->getText() << endl;
		cout << "count :" << number++ <<endl;
	}
	catch (zxing::Exception& e)
	{
		// cerr << "Error: " << e.what() << endl;
	}
}

void showImage(cv::Mat image,string name)
{
	cv::namedWindow(name);
	cv::imshow(name,image);
}
int main(int argc, char** argv)
{

	MultiFormatReader reader;
	int target_width = 640;//width * 4 / 5;
	int target_height = 480;//height * 4 / 5;

	IplImage *crop = cvCreateImage(cvSize(target_width, target_height), IPL_DEPTH_8U, 1);
	if (!crop)
	{
		cout<<"ERROR: allocating image"<<endl;
		return -1;
	}
//	cv::Mat image_bmp_data(480,640,CV_8UC3,cv::Scalar(0,0,0));
	cv::Mat gray;//(480,640,CV_8UC3,cv::Scalar(0,0,0));


    //cv::Mat frame = cv::imread("/nfsroot/rootfs/home/blackbox/qrcode-5/01.png");
	//cv::Mat frame = cv::imread("/nfsroot/rootfs/home/blackbox/code128-3/1.png");
	cv::Mat frame = cv::imread("/nfsroot/rootfs/home/blackbox/pdf417-2/01.png");

    //cv::Mat frame = cv::imread("/nfsroot/rootfs/home/v4l2/3.bmp");
    if(frame.data == NULL)
    {
    	cout << "Can not open picture"<<endl;
    	return 0;
    }
 //   showImage(frame,"srouce");

	use_global = use_hybrid = true;
	cout<< frame.cols <<endl;
	cout << frame.rows <<endl;
	cv::cvtColor(frame,gray,CV_RGB2GRAY);   //转为灰度图像
	cv::imwrite("/home/v4l2/01.bmp",gray);
//	showImage(gray,"gray");

//	cv::waitKey(0);

	cv::Mat image;

	// add contrast
	//matGray.convertTo(image, -1, 2.0, 0);	// add contrast, keep brightness

	gray.convertTo(image, -1, 2.0, 0);	// add contrast, keep brightness
	cv::imwrite("/home/v4l2/02.bmp",image);
	// sharpen image
	// use a gaussian smoothing filter and subtract the smoothed version from the original image

	cv::GaussianBlur(gray, image, cv::Size(11, 11), 10, 10);
	cv::imwrite("/home/v4l2/03.bmp",image);
	double alpha = 5.0; 	// alpha is the weight of the first array

	cv::addWeighted(gray, alpha, image, 1.0 - alpha, 0, image);
	cv::imwrite("/home/v4l2/04.bmp",image);
	*crop = gray;

//	decode_imageMat(&reader,gray);
	decode_image(&reader, crop);
//	cv::waitKey(0);

  return 0;
}
